#define DEBUG

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

using Crainiate.Data.Persistence.Providers;

namespace Crainiate.Data.Persistence.Tools
{
    public partial class DeploymentForm: Form
    {
        //Property variables
        private Deployment _deployment;
        private Context _context;
        private Assembly _assembly;
        private string[] _databases;

        //Working variables
        private Dictionary<Type, StringBuilder> _log;
        private Dictionary<Type, Form> _forms;
        private int _deployed;
        private int _exceptions;
        private int _validated;

        private Bitmap _bitmap;

        public DeploymentForm()
        {
            InitializeComponent();
            _log = new Dictionary<Type, StringBuilder>();
            _forms = new Dictionary<Type, Form>();
        }

        public Deployment Deployment
        {
            get
            {
                return _deployment;
            }
            set
            {
                _deployment = value;

                //Add event handlers to the deployment object
                if (_deployment != null)
                {
                    _deployment.AddTable += new AddTableEventHandler(Deployment_AddTable);
                    _deployment.AddReference += new ReferenceEventHandler(Deployment_AddReference);
                    _deployment.ChangeTable += new ChangeTableEventHandler(Deployment_ChangeTable);
                    _deployment.PreDeployType += new PreDeployTypeHandler(Deployment_PreDeployType);
                    _deployment.PostDeployType += new PostDeployTypeHandler(Deployment_PostDeployType);
                    _deployment.PreValidateType += new PreValidateTypeEventHandler(Deployment_PreValidateType);
                    _deployment.PostValidateType += new PostValidateTypeEventHandler(Deployment_PostValidateType);
                    _deployment.DeployTypeException += new DeployTypeExceptionHandler(Deployment_DeployTypeException);
                    _deployment.PostDeployAssembly += new DeployAssemblyHandler(Deployment_PostDeployAssembly);
                }
            }
        }

        public Context Context
        {
            get
            {
                return _context;
            }
            set
            {
                _context = value;
            }
        }

        public Assembly Assembly
        {
            get
            {
                return _assembly;
            }
            set
            {
                _assembly = value;
            }
        }

        public string[] Databases
        {
            get
            {
                return _databases;
            }
            set
            {
                _databases = value;
            }
        }

        private void cmdClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            LoadTypes();
        }

        private void LoadTypes()
        {
            listView1.Items.Clear();
            _log.Clear();
            _forms.Clear();

            int types = 0;

            //Load types into list
            foreach (Type type in _assembly.GetTypes())
            {
                if (Deployment.IsDeployable(type))
                {
                    ListViewItem item = new ListViewItem();
                    item.Checked = true;
                    item.Tag = type;
                    listView1.Items.Add(item);

                    ListViewItem.ListViewSubItem status = new ListViewItem.ListViewSubItem();
                    status.Text = "Pending";
                    status.Name = "Status";
                    item.SubItems.Add(status);

                    //Name
                    ListViewItem.ListViewSubItem name = new ListViewItem.ListViewSubItem();
                    name.Text = _deployment.GetTableName(type);
                    item.SubItems.Add(name);

                    //Type
                    ListViewItem.ListViewSubItem typeName = new ListViewItem.ListViewSubItem();
                    typeName.Text = type.FullName;
                    item.SubItems.Add(typeName);

                    //Add the description as a blank item
                    ListViewItem.ListViewSubItem desc = new ListViewItem.ListViewSubItem();
                    item.SubItems.Add(desc);

                    //Create a log string builder
                    _log.Add(type, new StringBuilder());

                    types++;
                }
            }

            _bitmap = listView1.Images[0];
            label1.Text = types.ToString() + " types loaded.";
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            _deployed = 0;
            _exceptions = 0;
            _validated = 0;

            this.Cursor = Cursors.WaitCursor;
            btnStart.Enabled = false;

            //Deploy!
            _deployment.Deploy(_assembly);

            btnStart.Enabled = true;
            this.Cursor = Cursors.Arrow;
        }

        private void Deployment_PreValidateType(object sender, PreValidateTypeEventArgs e)
        {
            ListViewItem item = FindItem(e.Type);
            if (item.Checked)
            {
                item.SubItems[1].Text = "Validating";
                _log[e.Type].Append("Validation started at ");
                _log[e.Type].Append(DateTime.Now.ToShortTimeString());
            }
            else
            {
                item.SubItems[1].Text = "Cancelled";
                item.SubItems[4].Text = "Deployment cancelled. ";
                e.Cancel = true;
                _log[e.Type].Append("Deployment cancelled.");
            }
            _log[e.Type].Append("\r\n");
        }

        private void Deployment_PostValidateType(object sender, PostValidateTypeEventArgs e)
        {
            ListViewItem item = FindItem(e.Type);

            _log[e.Type].Append("Validation ended at ");
            _log[e.Type].Append(DateTime.Now.ToShortTimeString());
            _log[e.Type].Append("\r\n");

            if (e.Exists)
            {
                if (e.Changed)
                {
                    item.SubItems[4].Text = "Requires changes. ";
                    _log[e.Type].Append("Exists and requires changes.\r\n");
                }
                else
                {
                    item.SubItems[1].Text = "Validated";
                    
                    item.SubItems[4].Text = "Does not require changes. ";
                    _log[e.Type].Append("Exists and does not require changes.\r\n");
                }
            }
            else
            {
                item.SubItems[4].Text = "Table Not found. ";
                _log[e.Type].Append("Underlying table(s) not found.\r\n");
            }
            _validated += 1;
        }

        private void Deployment_PreDeployType(object sender, PreDeployTypeEventArgs e)
        {
            ListViewItem item = FindItem(e.Type);
            item.SubItems[1].Text = "In Progress";

            _log[e.Type].Append("Deployment started at ");
            _log[e.Type].Append(DateTime.Now.ToShortTimeString());
            _log[e.Type].Append("\r\n");

            listView1.Invalidate(item.Bounds);
            listView1.Update(); 

            label1.Text = "Deploying type " + e.Type.FullName;
            _bitmap = listView1.Images[1];
            ctlFeedbackPanel.Invalidate();
        }

        private void Deployment_PostDeployType(object sender, PostDeployTypeEventArgs e)
        {
            ListViewItem item = FindItem(e.Type);
            item.SubItems[1].Text = "Deployed";

            if (_deployment.DeploymentType == DeploymentType.Deploy)
            {
                _log[e.Type].Append("Script executed successfully.");
                _log[e.Type].Append("\r\n");
            }

            _log[e.Type].Append("Deployment ended at ");
            _log[e.Type].Append(DateTime.Now.ToShortTimeString());
            _log[e.Type].Append("\r\n");

            _deployed += 1;
        }

        private void Deployment_DeployTypeException(object sender, DeployTypeExceptionEventArgs e)
        {
            ListViewItem item = FindItem(e.Type);
            item.SubItems[1].Text = "Exception";

            item.SubItems[4].Text += e.Exception.Message;

            _log[e.Type].Append("Exception occurred: ");
            _log[e.Type].Append(e.Exception.Message);
            _log[e.Type].Append("\r\n\r\n");

            _log[e.Type].Append(e.Script);
            _log[e.Type].Append("\r\n\r\n");

            e.Exception = null;
            _exceptions = 1;
        }

        private void Deployment_ChangeTable(object sender, ChangeTableEventInfo e)
        {
            ListViewItem item = FindItem(e.Type);

            if (_deployment.DeploymentType == DeploymentType.Deploy)
            {
                item.SubItems[4].Text += "Table changed. ";

                _log[e.Type].Append("Table changed.");
                _log[e.Type].Append("\r\n");
            }
            else
            {
                item.SubItems[4].Text += "Table will be changed. ";

                _log[e.Type].Append("Table to change.");
                _log[e.Type].Append("\r\n");
            }
        }

        private void Deployment_AddReference(object sender, ReferenceEventInfo e)
        {
            ListViewItem item = FindItem(e.Type);

            if (_deployment.DeploymentType == DeploymentType.Deploy)
            {
                item.SubItems[4].Text += "Added reference. ";

                _log[e.Type].Append("Reference added.");
                _log[e.Type].Append("\r\n");
            }
            else
            {
                item.SubItems[4].Text += "Reference to be added. ";

                _log[e.Type].Append("Reference to be added.");
                _log[e.Type].Append("\r\n");
            }
        }

        private void Deployment_AddTable(object sender, AddTableEventInfo e)
        {
            ListViewItem item = FindItem(e.Type);

            if (_deployment.DeploymentType == DeploymentType.Deploy)
            {
                item.SubItems[4].Text += "Added table. ";

                _log[e.Type].Append("Table added.");
                _log[e.Type].Append("\r\n");
            }
            else
            {
                item.SubItems[4].Text += "Table to be added. ";

                _log[e.Type].Append("Table to be added.");
                _log[e.Type].Append("\r\n");
            }
        }

        private void Deployment_PostDeployAssembly(object sender, DeployAssemblyEventArgs e)
        {
            StringBuilder builder = new StringBuilder();

            //Validated
            builder.Append(_validated.ToString());

            if (_validated == 1)
            {
                builder.Append(" type was validated");
            }
            else
            {
                builder.Append(" types were validated");
            }

            if (_deployment.DeploymentType == DeploymentType.Validate)
            {
                builder.Append(". ");
            }
            else
            {
                builder.Append(", ");

                //Deployed
                builder.Append(_deployed.ToString());

                if (_deployed == 1)
                {
                    builder.Append(" was");
                }
                else
                {
                    builder.Append(" were");
                }

                builder.Append(" deployed successfully with ");
                builder.Append(_exceptions.ToString());

                //Exceptions
                if (_exceptions == 1)
                {
                    builder.Append(" exception.");
                }
                else
                {
                    builder.Append(" exceptions.");
                }
            }

            label1.Text = builder.ToString();

            _bitmap = (_exceptions > 0)? listView1.Images[3]: listView1.Images[2] ;
            ctlFeedbackPanel.Invalidate();
        }


        private ListViewItem FindItem(Type type)
        {
            foreach (ListViewItem item in listView1.Items)
            {
                if (item.SubItems[3].Text == type.FullName) return item;
            }
            return null;
        }

        private void listView1_ItemActivate(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count > 0)
            {
                ListViewItem item = listView1.SelectedItems[0];
                Type type = item.Tag as Type;

                //Keep form references in a dictionary so they can be reshown
                if (_forms.ContainsKey(type))
                {
                    _forms[type].Show();
                }
                else
                {
                    LogForm form = new LogForm();
                    form.LogText = _log[type].ToString();
                    form.Text = type.FullName;
                    form.Type = type;
                    form.Disposed += new EventHandler(Form_Disposed);

                    _forms.Add(type, form);
                    form.Show(this);
                }
            }
        }

        private void Form_Disposed(object sender, EventArgs e)
        {
            LogForm form = sender as LogForm;

            _forms.Remove(form.Type);
        }

        private void ctlFeedbackPanel_Paint(object sender, PaintEventArgs e)
        {
        if (_bitmap != null) e.Graphics.DrawImageUnscaled(_bitmap, new Point(4,4));
        }
    }
}